<template>
  <div class="uploader-panel">
    <div class="uploader-title">
      <span>上传任务</span>
      <span class="tips">（仅展示本次上传任务）</span>
    </div>
    <div class="file-list">
      <div v-for="(item, index) in fileList" class="file-item" :key="item.uid">
  <div class="upload-panel">
    <div class="file-name1">{{ item.fileName }}</div>
    <!-- 显示上传进度 -->
    <el-progress :percentage="item.uploadProgress" />
    
    <!-- 显示上传完成的状态 -->
    <div class="upload-status">
      <!-- 文件上传大小和速度 -->
      <div class="upload-info">
        <span>{{ sizeTostr(item.uploadSize) }} / {{ sizeTostr(item.totalSize) }}</span>
        <span class="upload-speed">{{ item.uploadSpeed }} MB/s</span>
      </div>

      <!-- 状态描述 -->
      <span
        :class="['iconfont', 'icon-' + (STATUS[item.status]?.icon || 'default-icon')]"
        :style="{ color: STATUS[item.status]?.color || '#ccc' }"
      >
        {{ STATUS[item.status]?.desc }}
      </span>
    </div>
  </div>
  <!-- 操作按钮等 -->

        <div class="op">
          <!-- MD5 进度 -->
          <el-progress
            type="circle"
            :width="50"
            :percentage="item.md5Progress"
            v-if="item.status == STATUS.init.value"
          />
          <div class="op-btn">
            <!-- 上传暂停/恢复按钮 -->
            <span v-if="item.status === STATUS.uploading.value">
              <icon
                :width="28"
                class="btn-item"
                iconName="upload"
                v-if="item.pause"
                title="上传"
                @click="startUpload(item.uid)"
              ></icon>
              <icon
                :width="28"
                class="btn-item"
                iconName="pause"
                title="暂停"
                @click="pauseUpload(item.uid)"
                v-else
              ></icon>
            </span>
            <!-- 删除按钮 -->
            <icon
              :width="28"
              class="del btn-item"
              iconName="del"
              title="删除"
              v-if="
                item.status != STATUS.init.value &&
                item.status != STATUS.upload_finish.value &&
                item.status != STATUS.upload_seconds.value
              "
              @click="delUpload(item.uid, index)"
            ></icon>
            <!-- 清除按钮 -->
            <icon
              :width="28"
              class="clean btn-item"
              iconName="clean"
              title="清除"
              v-if="
                item.status == STATUS.upload_finish.value ||
                item.status == STATUS.upload_seconds.value
              "
              @click="delUpload(item.uid, index)"
            ></icon>
          </div>
        </div>
      </div>
      <div v-if="fileList.length == 0">
        <NoData msg="暂无上传任务"></NoData>
      </div>
    </div>
  </div>
</template>


<script setup>
import {
  getCurrentInstance,
  onMounted,
  reactive,
  computed,
  onBeforeUnmount,
  ref,
  watch,
  nextTick,
} from "vue";
import SparkMD5 from "spark-md5";
import eventBus from "@/js/eventBus";
const { proxy } = getCurrentInstance();
const fileList = ref([]);

onMounted(() => {
  eventBus.on("uploadProgress", ({ fileName, progress, uploadSize, totalSize, uploadSpeed }) => {
    const file = fileList.value.find(item => item.fileName === fileName);
    if (file) {
        file.uploadProgress = progress;
        file.uploadSize = uploadSize;  // 更新当前上传的大小
        file.totalSize = totalSize;    // 更新文件总大小
        file.uploadSpeed = uploadSpeed; // 更新上传速度
    } else {
        fileList.value.push({
            fileName,
            uploadProgress: progress,
            uploadSize: uploadSize,
            totalSize: totalSize,
            uploadSpeed: uploadSpeed,
            status: STATUS.uploading.value,
        });
    }
});


  eventBus.on("uploadCompleted", ({ fileName }) => {
  const file = fileList.value.find((item) => item.fileName === fileName);
  if (file) {
    file.uploadProgress = 100;  // 更新进度条为100
    file.status = STATUS.upload_finish.value;  // 更新状态为上传完成
  }
});



  eventBus.on("uploadFailed", ({ fileName, error }) => {
    const file = fileList.value.find(item => item.fileName === fileName);
    if (file) {
      file.status = `失败: ${error}`;
    }
  });
});

onBeforeUnmount(() => {
  eventBus.off("uploadProgress");
  eventBus.off("uploadCompleted");
  eventBus.off("uploadFailed");
});

const STATUS = {
  emptyfile: { value: "emptyfile", desc: "文件为空", color: "#F75000", icon: "close" },
  fail: { value: "fail", desc: "上传失败", color: "#F75000", icon: "close" },
  init: { value: "init", desc: "解析中", color: "#e6a23c", icon: "clock" },
  uploading: { value: "uploading", desc: "上传中", color: "#409eff", icon: "upload" },
  upload_finish: { value: "upload_finish", desc: "上传完成", color: "#67c23a", icon: "ok" },
  upload_seconds: { value: "upload_seconds", desc: "秒传", color: "#67c23a", icon: "ok" },
};

const chunkSize = 1024 * 1024 * 5;
const delList = ref([]);

const addFile = async (file, filePid) => {
  const fileItem = {
    file: file,
    uid: file.uid,
    md5Progress: 0,
    md5: null,
    fileName: file.name,
    status: STATUS.init.value,
    uploadSize: 0,
    totalSize: file.size,
    uploadProgress: 0,
    pause: false,
    chunkIndex: 0,
    filePid: filePid,
    errorMsg: null,
    uploadSpeed: 0, // 添加上传速度字段
    lastUpdateTime: Date.now(),
  };
  fileList.value.unshift(fileItem);
  if (fileItem.totalSize == 0) {
    fileItem.status = STATUS.emptyfile.value;
    return;
  }
  let md5FileUid = await computeMD5(fileItem);
  if (md5FileUid == null) {
    return;
  }
  uploadFile(md5FileUid);
};

const startUpload = (uid) => {
  let currentFile = getFileByUid(uid);
  currentFile.pause = false;
  uploadFile(uid, currentFile.chunkIndex);
};

const pauseUpload = (uid) => {
  let currentFile = getFileByUid(uid);
  currentFile.pause = true;
};

const delUpload = (uid, index) => {
  delList.value.push(uid);
  fileList.value.splice(index, 1);
};

const emit = defineEmits(["uploadCallback"]);

const uploadFile = async (uid, chunkIndex) => {
  chunkIndex = chunkIndex ? chunkIndex : 0;
  // 分片上传
  let currentFile = getFileByUid(uid);
  const file = currentFile.file;
  const fileSize = currentFile.totalSize;
  const chunks = Math.ceil(fileSize / chunkSize);

  for (let i = chunkIndex; i < chunks; i++) {
    let delIndex = delList.value.indexOf(uid);
    if (delIndex != -1) {
      delList.value.splice(delIndex, 1);
      break;
    }
    currentFile = getFileByUid(uid);
    if (currentFile.pause) {
      break;
    }

    let start = i * chunkSize;
    let end = start + chunkSize >= fileSize ? fileSize : start + chunkSize;
    let chunkFile = file.slice(start, end);

    let uploadResult = await proxy.Request({
      url: "/file/uploadFile",
      showLoading: false,
      dataType: "file",
      params: {
        file: chunkFile,
        fileName: file.name,
        fileMd5: currentFile.md5,
        chunkIndex: i,
        chunks: chunks,
        fileId: currentFile.fileId,
        filePid: currentFile.filePid,
      },
      showError: false,
      errorCallback: (errorMsg) => {
        currentFile.status = STATUS.fail.value;
        currentFile.errorMsg = errorMsg;
      },
      uploadProgressCallback: (event) => {
        let loaded = event.loaded;
        if (loaded > fileSize) {
          loaded = fileSize;
        }
        currentFile.uploadSize = i * chunkSize + loaded;
        currentFile.uploadProgress = Math.floor(
          (currentFile.uploadSize / fileSize) * 100
        );
      },
    });

    if (!uploadResult) {
      currentFile.status = STATUS.fail.value;
      break;
    }

    if (uploadResult.data.status === STATUS.upload_finish.value) {
      currentFile.uploadProgress = 100;
      currentFile.status = STATUS.upload_finish.value; 
      emit("uploadCallback");
      break;
    } else if (uploadResult.data.status === "processing") {
      currentFile.status = "处理中";
    }
  }
};



const computeMD5 = (fileItem) => {
  let file = fileItem.file;
  let blobSlice =
    File.prototype.slice ||
    File.prototype.mozSlice ||
    File.prototype.webkitSlice;
  let chunks = Math.ceil(file.size / chunkSize);
  let currentChunk = 0;
  let spark = new SparkMD5.ArrayBuffer();
  let fileReader = new FileReader();
  let time = new Date().getTime();

  let loadNext = () => {
    let start = currentChunk * chunkSize;
    let end = start + chunkSize >= file.size ? file.size : start + chunkSize;
    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
  };

  loadNext();
  return new Promise((resolve, reject) => {
    let resultFile = getFileByUid(file.uid);
    fileReader.onload = (e) => {
      spark.append(e.target.result);
      currentChunk++;
      if (currentChunk < chunks) {
        let percent = Math.floor((currentChunk / chunks) * 100);
        resultFile.md5Progress = percent;
        loadNext();
      } else {
        let md5 = spark.end();
        spark.destroy(); //释放缓存
        resultFile.md5Progress = 100;
        resultFile.status = STATUS.uploading.value;
        resultFile.md5 = md5;
        resolve(fileItem.uid);
      }
    };
    fileReader.onerror = () => {
      resultFile.md5Progress = -1;
      resultFile.status = STATUS.fail.value;
      resolve(fileItem.uid);
    };
  }).catch((error) => {
    return null;
  });
};

const getFileByUid = (uid) => {
  let file = fileList.value.find((item) => {
    return item.file.uid === uid;
  });
  return file;
};

const sizeTostr = (size) => {
  var data = "";
  if (size < 0.1 * 1024) {
    data = size.toFixed(2) + "B";
  } else if (size < 0.1 * 1024 * 1024) {
    data = (size / 1024).toFixed(2) + "KB";
  } else if (size < 1024 * 1024 * 1024) {
    data = (size / (1024 * 1024)).toFixed(2) + "MB";
  } else {
    data = (size / (1024 * 1024 * 1024)).toFixed(2) + "GB";
  }
  var sizestr = data + "";
  var len = sizestr.indexOf(".");
  var dec = sizestr.substr(len + 1, 2);
  if (dec == "00") {
    return sizestr.substring(0, len) + sizestr.substr(len + 3, 2);
  }
  return sizestr;
};
</script>

<style lang="scss" scoped>
.uploader-panel {
  .uploader-title {
    border-bottom: 1px solid #ddd;
    line-height: 40px;
    padding: 0px 10px;
    font-size: 15px;
    .tips {
      font-size: 13px;
      color: rgb(169, 169, 169);
    }
  }
  .file-list {
    overflow: auto;
    padding: 10px 0px;
    min-height: calc(100vh / 3);
    max-height: calc(100vh - 300px);
    .file-item {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 3px 10px;
      background-color: #fff;
      border-bottom: 1px solid #ddd;
    }
    .file-item:nth-child(even) {
      background-color: #fcf8f4;
    }
    .upload-panel {
      flex: 1;
      .file-name1 {
        color: rgb(64, 62, 62);
      }
      .upload-status {
  display: flex;
  justify-content: space-between;  // 在行内分布上传速度和状态
  align-items: center;  // 保持垂直居中
  margin-top: 5px;
  width: 100%;
}

.upload-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: rgb(25, 23, 23);
}

.upload-speed {
  margin-left: 400px;  // 给上传速度加点左边距
  font-weight: bold;
  color: #409eff;
  white-space: nowrap;  // 保证上传速度文本不换行
}

.status {
  font-size: 13px;
  color: #67c23a;
  margin-left: 20px;  // 设置上传速度和状态之间的间距
  white-space: nowrap;  // 保证状态文本不换行
}

.iconfont {
  margin-right: 5px;  // 图标和状态文字之间有点间距
}






      .progress {
        height: 10px;
      }
    }
    .op {
      width: 100px;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      .op-btn {
        .btn-item {
          cursor: pointer;
        }
        .del,
        .clean {
          margin-left: 5px;
        }
        
      }
    }
  }
}

</style>

